From 3db7344f33616983ca0b17b48200cf735679f695 Mon Sep 17 00:00:00 2001 From: Kristian Rietveld Date: Sun, 11 Sep 2011 16:53:27 +0200 Subject: [PATCH] treemodelfilter: correct forgotten unref of parent on level destroy gtk_tree_model_filter_free_level() should always release ref on parent unless the parent has been removed in the child model. The unit tests added in the previous commit test this. --- gtk/gtktreemodelfilter.c | 81 +++++++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 30 deletions(-) diff --git a/gtk/gtktreemodelfilter.c b/gtk/gtktreemodelfilter.c index b401a0df79..df09ddaa96 100644 --- a/gtk/gtktreemodelfilter.c +++ b/gtk/gtktreemodelfilter.c @@ -423,7 +423,8 @@ static void gtk_tree_model_filter_build_level (GtkTr static void gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter, FilterLevel *filter_level, - gboolean unref, + gboolean unref_self, + gboolean unref_parent, gboolean unref_external); static GtkTreePath *gtk_tree_model_filter_elt_get_path (FilterLevel *level, @@ -602,7 +603,7 @@ gtk_tree_model_filter_finalize (GObject *object) gtk_tree_path_free (filter->priv->virtual_root); if (filter->priv->root) - gtk_tree_model_filter_free_level (filter, filter->priv->root, TRUE, FALSE); + gtk_tree_model_filter_free_level (filter, filter->priv->root, TRUE, TRUE, FALSE); g_free (filter->priv->modify_types); @@ -892,7 +893,7 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter, if (empty && (parent_level && parent_level->ext_ref_count == 0)) { - gtk_tree_model_filter_free_level (filter, new_level, FALSE, FALSE); + gtk_tree_model_filter_free_level (filter, new_level, FALSE, TRUE, FALSE); return; } @@ -931,7 +932,8 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter, static void gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter, FilterLevel *filter_level, - gboolean unref, + gboolean unref_self, + gboolean unref_parent, gboolean unref_external) { GSequenceIter *siter; @@ -947,9 +949,17 @@ gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter, FilterElt *elt = g_sequence_get (siter); if (elt->children) - gtk_tree_model_filter_free_level (filter, - FILTER_LEVEL (elt->children), - unref, unref_external); + { + /* If we recurse and unref_self == FALSE, then unref_parent + * must also be FALSE (otherwise a still unref a node in this + * level). + */ + gtk_tree_model_filter_free_level (filter, + FILTER_LEVEL (elt->children), + unref_self, + unref_self == FALSE ? FALSE : unref_parent, + unref_external); + } if (unref_external) { @@ -962,13 +972,13 @@ gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter, while (elt->ext_ref_count > 0) gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), &f_iter, - TRUE, unref); + TRUE, unref_self); } } /* Release the reference on the first item. */ - if (unref) + if (unref_self) { GtkTreeIter f_iter; @@ -1000,17 +1010,14 @@ gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter, if (filter_level->parent_elt) { /* Release reference on parent */ - if (unref) - { - GtkTreeIter parent_iter; + GtkTreeIter parent_iter; - parent_iter.stamp = filter->priv->stamp; - parent_iter.user_data = filter_level->parent_level; - parent_iter.user_data2 = filter_level->parent_elt; + parent_iter.stamp = filter->priv->stamp; + parent_iter.user_data = filter_level->parent_level; + parent_iter.user_data2 = filter_level->parent_elt; - gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), - &parent_iter, FALSE, TRUE); - } + gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), + &parent_iter, FALSE, unref_parent); filter_level->parent_elt->children = NULL; } @@ -1052,8 +1059,8 @@ gtk_tree_model_filter_prune_level (GtkTreeModelFilter *filter, if (elt->children) gtk_tree_model_filter_free_level (filter, - FILTER_LEVEL (elt->children), TRUE, - TRUE); + FILTER_LEVEL (elt->children), + TRUE, TRUE, TRUE); } /* For the first item, only drop the external references */ @@ -1316,7 +1323,7 @@ gtk_tree_model_filter_clear_cache_helper (GtkTreeModelFilter *filter, level->parent_level && level->parent_level != filter->priv->root && level->parent_level->ext_ref_count == 0) { - gtk_tree_model_filter_free_level (filter, level, TRUE, FALSE); + gtk_tree_model_filter_free_level (filter, level, TRUE, TRUE, FALSE); return; } } @@ -1693,7 +1700,7 @@ gtk_tree_model_filter_remove_elt_from_level (GtkTreeModelFilter *filter, * the case length == 1. */ if (elt->children) - gtk_tree_model_filter_free_level (filter, elt->children, TRUE, TRUE); + gtk_tree_model_filter_free_level (filter, elt->children, TRUE, TRUE, TRUE); /* If the first node is being removed, transfer, the reference */ if (elt == g_sequence_get (g_sequence_get_begin_iter (level->seq))) @@ -1751,7 +1758,7 @@ gtk_tree_model_filter_remove_elt_from_level (GtkTreeModelFilter *filter, level->parent_level->ext_ref_count > 0)) { /* Otherwise, the level can be removed */ - gtk_tree_model_filter_free_level (filter, level, TRUE, TRUE); + gtk_tree_model_filter_free_level (filter, level, TRUE, TRUE, TRUE); } else { @@ -1768,7 +1775,7 @@ gtk_tree_model_filter_remove_elt_from_level (GtkTreeModelFilter *filter, #endif if (elt->children) gtk_tree_model_filter_free_level (filter, elt->children, - TRUE, TRUE); + TRUE, TRUE, TRUE); } else { @@ -2422,7 +2429,7 @@ gtk_tree_model_filter_virtual_root_deleted (GtkTreeModelFilter *filter, * nodes will fail, since the respective nodes in the child model are * no longer there. */ - gtk_tree_model_filter_free_level (filter, filter->priv->root, FALSE, FALSE); + gtk_tree_model_filter_free_level (filter, filter->priv->root, FALSE, TRUE, FALSE); gtk_tree_model_filter_increment_stamp (filter); @@ -2601,20 +2608,34 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model, while (elt->ext_ref_count > 0) gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter, TRUE, FALSE); - while (elt->ref_count > 0) - gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter, - FALSE, FALSE); if (g_sequence_get_length (level->seq) == 1) { + if (elt->children) + /* If this last node has children, then the recursion in free_level + * will release this reference. + */ + while (elt->ref_count > 1) + gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter, + FALSE, FALSE); + else + while (elt->ref_count > 0) + gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter, + FALSE, FALSE); + /* kill level */ - gtk_tree_model_filter_free_level (filter, level, FALSE, FALSE); + gtk_tree_model_filter_free_level (filter, level, FALSE, TRUE, FALSE); } else { GSequenceIter *tmp; gboolean is_first; + /* Release last references, if needed */ + while (elt->ref_count > 0) + gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter, + FALSE, FALSE); + lookup_elt_with_offset (level->seq, elt->offset, &siter); is_first = g_sequence_get_begin_iter (level->seq) == siter; @@ -3638,7 +3659,7 @@ gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter, /* reset our state */ if (filter->priv->root) gtk_tree_model_filter_free_level (filter, filter->priv->root, - TRUE, FALSE); + TRUE, TRUE, FALSE); filter->priv->root = NULL; g_object_unref (filter->priv->child_model); -- 2.30.2